/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2017.07.18                                          *
 * Description:      the structure to save fluent mesh information       *
 *                                                                       *
 *************************************************************************/
//declaration header
#include "COMMON/mesh/FluentMesh.h"

//C++ headers
#include <cassert>
#include <cstdlib>
#include <iostream>
#include <fstream>
#include <sstream>
#include <set>
#include <map>

//COMMON headers
#include "COMMON/tbox/Tokenizer.h"

namespace FreeMAPs
{

FluentMesh::FluentMesh(
    const std::string& file):
    d_fluent_mesh_file_name (file)
{}



FluentMesh::~FluentMesh()
{}



void
FluentMesh::clear()
{
    d_content.clear();
    d_tokens.clear();
    d_mesh_nodes.clear();
    d_mesh_cells.clear();
    d_mesh_faces.clear();
    d_mesh_regions.clear();
}



int
FluentMesh::read()
{
    int result = 0;
    result += read_fluent_mesh_file();
    result += lex_pre_process();
    result += do_lex();

    result += do_parse();

    result += post_process();

    return result;
}



bool
FluentMesh::is_region (
        const std::string& region_type)
{
    if (region_type == "fluid" || region_type == "solid")
    {
        return true;
    }
    else
    {
        return false;
    }
}



void
FluentMesh::export_vtk(
    const std::string& vtk_file_name) const
{
    std::ofstream out (vtk_file_name.c_str(), std::ofstream::trunc);

    out << "# vtk DataFile Version 3.0"      <<'\n';
    out << "Date Generated by Genius TCAD"   <<'\n';
    out << "ASCII"                           <<'\n';
    out << "DATASET UNSTRUCTURED_GRID"       <<'\n';
    out << "POINTS " << d_mesh_node_num      << " float" << '\n';

    // write out nodal data
    for (unsigned int n = 0; n < d_mesh_nodes.size(); ++n)
    {
        out << d_mesh_nodes[n].x << " " << d_mesh_nodes[n].y << " " << d_mesh_nodes[n].z << '\n';
    }

    out << std::endl;

    int cell_size = d_mesh_cell_num;
    for (unsigned int n = 0; n < d_mesh_cells.size(); ++n)
    {
        cell_size += d_mesh_cells[n].nodes.size();
    }

    out << "CELLS " << d_mesh_cell_num << " " << cell_size << '\n';

    for (unsigned int n = 0; n < d_mesh_cells.size(); ++n)
    {
        const Cell_t& cell = d_mesh_cells[n];
        out << cell.nodes.size()<<" ";

        switch (cell.type)
        {
        case 1:
            out << cell.nodes[0]-1<<" "<< cell.nodes[1]-1<<" "<< cell.nodes[2]-1;  //VTK_TRIANGLE;
            break;
        case 2:
            out << cell.nodes[0]-1<<" "<< cell.nodes[1]-1<<" "<< cell.nodes[2]-1<<" "<< cell.nodes[3]-1; //VTK_TETRA;
            break;
        case 3:
            out << cell.nodes[0]-1<<" "<< cell.nodes[1]-1<<" "<< cell.nodes[2]-1<<" "<< cell.nodes[3]-1;  //VTK_QUAD;
            break;
        case 4:
            out << cell.nodes[0]-1<<" "<< cell.nodes[1]-1<<" "<< cell.nodes[2]-1<<" "<< cell.nodes[3]-1<<" "
                << cell.nodes[4]-1<<" "<< cell.nodes[5]-1<<" "<< cell.nodes[6]-1<<" "<< cell.nodes[7]-1; //VTK_HEXAHEDRON;
            break;
        case 5:
            out << cell.nodes[3]-1<<" "<< cell.nodes[2]-1<<" "<< cell.nodes[1]-1<<" "<< cell.nodes[0]-1<<" "<< cell.nodes[4]-1; //VTK_PYRAMID;
            break;
        case 6:
            out << cell.nodes[0]-1<<" "<< cell.nodes[2]-1<<" "<< cell.nodes[1]-1<<" "
                << cell.nodes[3]-1<<" "<< cell.nodes[5]-1<<" "<< cell.nodes[4]-1; //VTK_WEDGE;
            break;
        default:
            {
                std::cerr<<"element type "<<cell.type<<" not implemented"<<std::endl;
            }
        }
        out << std::endl;
    }
    out << std::endl;

    out << "CELL_TYPES " << d_mesh_cell_num << '\n';
    for (unsigned int n = 0; n < d_mesh_cells.size(); ++n)
    {
        unsigned int celltype;
        switch (d_mesh_cells[n].type)
        {
        case 1:
            celltype = 5;  //VTK_TRIANGLE;
            break;
        case 2:
            celltype = 10; //VTK_TETRA;
            break;
        case 3:
            celltype = 9;  //VTK_QUAD;
            break;
        case 4:
            celltype = 12; //VTK_HEXAHEDRON;
            break;
        case 5:
            celltype = 14; //VTK_PYRAMID;
            break;
        case 6:
            celltype = 13; //VTK_WEDGE;
            break;

        default:
            {
                std::cerr << "element type " << d_mesh_cells[n].type << " not implemented" << std::endl;
            }
        }
        out << celltype << std::endl;
    }

    out << std::endl;


    // region information
    out << "CELL_DATA "           << d_mesh_cell_num <<'\n';
    out << "SCALARS region int 1" << '\n';
    out << "LOOKUP_TABLE default" << '\n';
    for (unsigned int n = 0; n < d_mesh_cells.size(); ++n)
    {
        out << d_mesh_cells[n].region<<'\n';
    }
    out << std::endl;

    out.close();
}



int
FluentMesh::read_fluent_mesh_file()
{
    /*
     * read file into _content
     */

    // first step, read file into _content

    std::ifstream ftmp (d_fluent_mesh_file_name.c_str(), std::ios::in);

    if (!ftmp.good())
    {
        std::cerr << "Open Fluent msh file error." << std::endl;
        return 1;
    }

    std::getline (ftmp, d_content, '\0');

    ftmp.close();
    return 0;
}



int
FluentMesh::lex_pre_process()
{
    /*
     * scan the mesh _content, do preprocess
     *
     * here we inset space between each '(' and ')' by replacing method, then white char can be used for spliter
     */
    replace_all (d_content, "(", " ( ");
    replace_all (d_content, ")", " ) ");

    return 0;
}



int
FluentMesh::do_lex()
{
    /*
     * here we call Tokenizer to split d_content into tokens
     */
    Tokenizer tokenizer (d_content);
    d_tokens = tokenizer.split();
    d_content.clear();

    return 0;
}



int
FluentMesh::do_parse()
{
    // grammar tree
    GrammarTreeNode * grammar_root = new GrammarTreeNode;

    GrammarTreeNode * grammar_current = grammar_root;
    for (size_t i = 0; i < d_tokens.size(); i++)
    {
        //std::cout << "token = " << d_tokens[i] << std::endl;
        if (d_tokens[i] == "(")
        {
            GrammarTreeNode * grammar_node = new GrammarTreeNode;
            grammar_node->parent = grammar_current;
            grammar_current->child.push_back(grammar_node);
            grammar_current = grammar_node;
        }
        else if (d_tokens[i] == ")")
        {
            grammar_current = grammar_current->parent;
        }
        else
        {
            grammar_current->content.push_back (d_tokens[i]);
        }
    }

    d_tokens.clear();

    // loop all the top-level groups, and do process
    const std::vector<GrammarTreeNode*>& groups = grammar_root->child;

    for (size_t g = 0; g < groups.size(); g++)
    {
        const GrammarTreeNode * group = groups[g];
        std::string key = group->content[0];
        if ( key == "2"  )                process_mesh_dimension (group);
        if ( key == "10" )                process_mesh_nodes     (group);
        if ( key == "12" )                process_mesh_cells     (group);
        if ( key == "13" )                process_mesh_faces     (group);
        if ( key == "45" || key == "39" ) process_mesh_regions   (group);
    }

    // free grammar tree
    delete grammar_root;

    return 0;
}



void
FluentMesh::process_mesh_dimension(
    const GrammarTreeNode* block)
{
    assert (block->content.size() == 2);
    d_mesh_dimension = dec_string_to_int (block->content[1]);
    //std::cout << d_mesh_dimension << std::endl;
}



void
FluentMesh::process_mesh_nodes(
    const GrammarTreeNode* block)
{
    if (block->child.empty()) return;

    const GrammarTreeNode* head = block->child[0];
    int region = hex_string_to_int (head->content[0]);
    int begin  = hex_string_to_int (head->content[1]);
    int end    = hex_string_to_int (head->content[2]);

    if (region == 0) { d_mesh_node_num = end; return; } // total number of nodes

    //int type = dec_string_to_int(head->content[3]);
    int N    = d_mesh_dimension;
    int size = end-begin + 1;
    if (head->content.size() == 5) N = dec_string_to_int (head->content[4]);

    if (block->child.size() == 1) return;

    // get nodes from each region
    const GrammarTreeNode * body = block->child[1];
    for (int i = 0; i < size; i++)
    {
        Node_t node;

        node.x = string_to_double (body->content[N*i+0]);
        node.y = string_to_double (body->content[N*i+1]);
        if(N==3)
            node.z = string_to_double (body->content[N*i+2]);
        else
            node.z = 0.0;

        node.region = region;
        node.index  = begin+i;

        d_mesh_nodes.push_back (node);
        //std::cout << node.index << " " << node.x << " " << node.y << " " << node.z << std::endl;
    }
}



void
FluentMesh::process_mesh_faces(
    const GrammarTreeNode* block)
{
    if(block->child.empty()) return;

    const GrammarTreeNode* head = block->child[0];
    int region = hex_string_to_int (head->content[0]);
    int begin  = hex_string_to_int (head->content[1]);
    int end    = hex_string_to_int (head->content[2]);

    if(region == 0) { d_mesh_face_num = end; return; } // total number of faces

    //int type      = dec_string_to_int (head->content[3]);
    int face_type = dec_string_to_int (head->content[4]);

    int size = end-begin + 1;

    // get cells from each region
    const GrammarTreeNode* body = block->child[1];
    size_t count=0;
    if (face_type !=0 ) // region with unique face type
    {
        for (int i = 0; i < size; i++)
        {
            Face_t face;
            face.region = region;
            face.index = begin+i;
            face.type = face_type;
            for(int c=0; c<face_type; c++)//face.type equals to node number of this face
            {
                face.nodes.push_back (hex_string_to_int (body->content[count++])); // read face nodes
            }

            face.cell_right = hex_string_to_int (body->content[count++]); // face right cell
            face.cell_left  = hex_string_to_int (body->content[count++]); // face left cell

            d_mesh_faces.push_back(face);
        }
    }
    else // region with mixed face type
    {
        for (int i = 0; i < size; i++)
        {
            Face_t face;
            face.region = region;
            face.index = begin+i;
            face.type = dec_string_to_int (body->content[count++]);
            for(int c=0; c<face.type; c++) //face.type equals to node number of this face
            {
                face.nodes.push_back (hex_string_to_int (body->content[count++])); // read face nodes
            }

            face.cell_right = hex_string_to_int (body->content[count++]); // face right cell
            face.cell_left  = hex_string_to_int (body->content[count++]); // face left cell

            d_mesh_faces.push_back(face);
        }
    }

    assert (count == body->content.size() );
}



void
FluentMesh::process_mesh_cells(
    const GrammarTreeNode* block)
{
    if(block->child.empty()) return;

    const GrammarTreeNode* head = block->child[0];
    int region = hex_string_to_int (head->content[0]);
    int begin  = hex_string_to_int (head->content[1]);
    int end    = hex_string_to_int (head->content[2]);

    if(region == 0) { d_mesh_cell_num = end; return; } // total number of cells

    //int type      = dec_string_to_int (head->content[3]);
    int cell_type = dec_string_to_int (head->content[4]);

    int size = end-begin+1;
    if(cell_type !=0 ) // region with unique cell type
    {
        for(int i=0; i<size; i++)
        {
            Cell_t cell;
            cell.index = begin+i;
            cell.region = region;
            cell.type = cell_type;
            d_mesh_cells.push_back (cell);
        }
    }

    if (block->child.size() == 1) return;

    // get cells from each region
    const GrammarTreeNode* body = block->child[1];
    assert ((int)body->content.size() == size);

    for(int i=0; i<size; i++)
    {
        Cell_t cell;
        cell.index = begin+i;
        cell.region = region;
        cell.type = dec_string_to_int (body->content[i]);
        d_mesh_cells.push_back(cell);
    }

    //std::cout << region << " " << begin << " " << end << " " << type << " " << cell_type << std::endl;
}



void
FluentMesh::process_mesh_regions(
    const GrammarTreeNode* block)
{
    if(block->child.empty()) return;

    const GrammarTreeNode * head = block->child[0];

    Region_t region;
    region.index = dec_string_to_int (head->content[0]);
    region.type  = head->content[1];
    region.name  = head->content[2];
    d_mesh_regions.insert (std::make_pair (region.index, region));

    if( is_region(region.type) )
    {
        d_region_names.push_back(region.name);
    }
    //std::cout <<region.index << " " <<  region.type << " " << region.name << std::endl;
}



int
FluentMesh::post_process()
{
    /*
     * the stupid fluent msh format does not provide cell information
     * we must build node list of each cell from face
     */

    for(size_t f=0; f<d_mesh_faces.size(); f++)
    {
        const Face_t & face = d_mesh_faces[f];
        assert(face.index-1 == (int)f);
        if( face.cell_right > 0 ) d_mesh_cells[face.cell_right - 1].faces.push_back (face.index);
        if( face.cell_left  > 0 ) d_mesh_cells[face.cell_left  - 1].faces.push_back (-face.index);
    }

    for(size_t c=0; c<d_mesh_cells.size(); c++)
    {
        Cell_t& cell = d_mesh_cells[c];
        assert(cell.index-1 == (int)c);

        switch (cell.type)
        {
        case 1 :  // triangular
            {
                process_triangle (cell);
                break;
            }

        case 2 :  // tetrahedral
            {
                process_tetrahedron (cell);
                break;
            }

        case 3: // quadrilateral
            {
                process_quadrangle (cell);
                break;
            }

        case 4: // hexahedral
            {
                process_hexahedra (cell);
                break;
            }

        case 5: // pyramid
            {
                process_pyramid (cell);
                break;
            }

        case 6: //wedge
            {
                process_wedge (cell);
                break;
            }

        default: break;
        }
    }

    return 0;
}



void
FluentMesh::process_triangle (
    Cell_t& cell)
{
    std::map<int, Vertex2D> topo_link;

    for(size_t f=0; f<cell.faces.size(); f++)
    {
        int findex = cell.faces[f];
        const Face_t & face = d_mesh_faces[std::abs(findex)-1];

        if(findex >=0 )
        {
            Vertex2D & v0 = topo_link[face.nodes[0]-1];
            Vertex2D & v1 = topo_link[face.nodes[1]-1];
            v0.index = face.nodes[0];
            v1.index = face.nodes[1];
            v0.next = &v1;
        }
        else
        {
            Vertex2D & v0 = topo_link[face.nodes[1]-1];
            Vertex2D & v1 = topo_link[face.nodes[0]-1];
            v0.index = face.nodes[1];
            v1.index = face.nodes[0];
            v0.next = &v1;
        }
    }

    const Vertex2D * begin = &(topo_link.begin()->second);
    for(int i=0; i<3; i++)
    {
        cell.nodes.push_back(begin->index);
        begin = begin->next;
    }
}



void
FluentMesh::process_quadrangle(
    Cell_t& cell)
{
    std::map<int, Vertex2D> topo_link;

    for(size_t f=0; f<cell.faces.size(); f++)
    {
        int findex = cell.faces[f];
        const Face_t & face = d_mesh_faces[std::abs(findex)-1];

        if(findex >=0 )
        {
            Vertex2D & v0 = topo_link[face.nodes[0]];
            Vertex2D & v1 = topo_link[face.nodes[1]];
            v0.index = face.nodes[0];
            v1.index = face.nodes[1];
            v0.next = &v1;
        }
        else
        {
            Vertex2D & v0 = topo_link[face.nodes[1]];
            Vertex2D & v1 = topo_link[face.nodes[0]];
            v0.index = face.nodes[1];
            v1.index = face.nodes[0];
            v0.next = &v1;
        }
    }

    const Vertex2D * begin = &(topo_link.begin()->second);
    for(int i=0; i<4; i++)
    {
        cell.nodes.push_back(begin->index);
        begin = begin->next;
    }
}

/*
void FluentMesh::_process_tetrahedron(Cell_t& cell)
{
  int f0 = cell.faces[0];
  int f1 = cell.faces[1];
  const Face_t & face0 = _mesh_faces[std::abs(f0)-1]; assert(face0.type == 3);
  const Face_t & face1 = _mesh_faces[std::abs(f1)-1]; assert(face1.type == 3);
  if(f0 >=0 )
  {
    cell.nodes.push_back(face0.nodes[0]);
    cell.nodes.push_back(face0.nodes[1]);
    cell.nodes.push_back(face0.nodes[2]);
  }
  else
  {
    cell.nodes.push_back(face0.nodes[2]);
    cell.nodes.push_back(face0.nodes[1]);
    cell.nodes.push_back(face0.nodes[0]);
  }

  if(f1 >=0 )
  {
    cell.nodes.push_back(face1.nodes[0]);
    cell.nodes.push_back(face1.nodes[1]);
    cell.nodes.push_back(face1.nodes[2]);
  }
  else
  {
    cell.nodes.push_back(face1.nodes[2]);
    cell.nodes.push_back(face1.nodes[1]);
    cell.nodes.push_back(face1.nodes[0]);
  }

  __unsorted_remove_duplicates(cell.nodes);

  for(size_t i=0; i<cell.nodes.size(); i++)
    std::cout << cell.nodes[i] << " ";
  std::cout << std::endl;
}
*/


void
FluentMesh::process_tetrahedron(
    Cell_t& cell)
{
    int fbase=cell.faces[0]; // select as base

    std::set<int> nodes;
    for(size_t f=0; f<cell.faces.size(); f++)
    {
        int findex = cell.faces[f];
        const Face_t & face = d_mesh_faces[std::abs(findex)-1];
        nodes.insert(face.nodes.begin(),  face.nodes.end());
    }

    // 3 nodes on base
    const Face_t & face = d_mesh_faces[std::abs(fbase)-1];
    cell.nodes = face.nodes;
    if(fbase<0)  std::reverse(cell.nodes.begin(), cell.nodes.end());

    // the last node not on base
    for(size_t i=0; i<face.nodes.size(); i++)
        nodes.erase(face.nodes[i]);

    assert(nodes.size() == 1);
    cell.nodes.push_back(*nodes.begin());
}



void
FluentMesh::process_pyramid(
    Cell_t& cell)
{
    int fbase=0;  // select base

    std::set<int> nodes;
    for(size_t f=0; f<cell.faces.size(); f++)
    {
        int findex = cell.faces[f];
        const Face_t & face = d_mesh_faces[std::abs(findex)-1];
        nodes.insert(face.nodes.begin(),  face.nodes.end());
        if( face.type == 4 ) // quadrilateral
        {
            fbase = findex;
        }
    }

    // 4 nodes on base
    const Face_t & face = d_mesh_faces[std::abs(fbase)-1];
    cell.nodes = face.nodes;
    if(fbase<0)  std::reverse(cell.nodes.begin(), cell.nodes.end());

    // the last node not on base
    for(size_t i=0; i<face.nodes.size(); i++)
        nodes.erase(face.nodes[i]);

    assert(nodes.size() == 1);
    cell.nodes.push_back(*nodes.begin());
}



void
FluentMesh::process_hexahedra(
    Cell_t& cell)
{
    int fbase=cell.faces[0]; // select as base
    const Face_t & face = d_mesh_faces[std::abs(fbase)-1];

    std::set<int> nodes;
    nodes.insert(face.nodes.begin(),  face.nodes.end());

    std::map<int, int> edges;

    for(size_t f=1; f<cell.faces.size(); f++)
    {
        int findex = cell.faces[f];
        const Face_t & face = d_mesh_faces[std::abs(findex)-1];
        std::vector<bool> on_base; // face node on base flag
        for(size_t n=0; n<face.nodes.size(); n++)
        {
            if( nodes.find(face.nodes[n]) == nodes.end() )
                on_base.push_back(false);
            else
                on_base.push_back(true);
        }
        // edge (0, 1)
        if( (on_base[0] && on_base[1]) == false && (on_base[0] || on_base[1]) == true )
        {
            edges.insert( std::make_pair(face.nodes[0] , face.nodes[1]) );
            edges.insert( std::make_pair(face.nodes[1] , face.nodes[0]) );
        }

        // edge (1, 2)
        if( (on_base[1] && on_base[2]) == false && (on_base[2] || on_base[1]) == true )
        {
            edges.insert( std::make_pair(face.nodes[1] , face.nodes[2]) );
            edges.insert( std::make_pair(face.nodes[2] , face.nodes[1]) );
        }

        // edge (2, 3)
        if( (on_base[2] && on_base[3]) == false && (on_base[3] || on_base[2]) == true )
        {
            edges.insert( std::make_pair(face.nodes[2] , face.nodes[3]) );
            edges.insert( std::make_pair(face.nodes[3] , face.nodes[2]) );
        }

        // edge (3, 0)
        if( (on_base[3] && on_base[0]) == false && (on_base[0] || on_base[3]) == true )
        {
            edges.insert( std::make_pair(face.nodes[3] , face.nodes[0]) );
            edges.insert( std::make_pair(face.nodes[0] , face.nodes[3]) );
        }
    }

    // all the nodes
    cell.nodes = face.nodes;
    if(fbase<0)  std::reverse(cell.nodes.begin(), cell.nodes.end());

    for(size_t i=0; i<4; i++)
    {
        cell.nodes.push_back(edges.find(cell.nodes[i])->second);
    }
}



void
FluentMesh::process_wedge(
    Cell_t& cell)
{
    int fbase;
    for(size_t f=0; f<cell.faces.size(); f++)
    {
        int findex = cell.faces[f];
        const Face_t & face = d_mesh_faces[std::abs(findex)-1];
        if(face.type == 3) // triangular
            fbase = findex;
    }
    const Face_t & face = d_mesh_faces[std::abs(fbase)-1];

    std::set<int> nodes;
    nodes.insert(face.nodes.begin(),  face.nodes.end());

    std::map<int, int> edges;

    for(size_t f=0; f<cell.faces.size(); f++)
    {
        int findex = cell.faces[f];
        if(findex == fbase) continue;

        const Face_t & face = d_mesh_faces[std::abs(findex)-1];
        std::vector<bool> on_base; // face node on base flag
        for(size_t n=0; n<face.nodes.size(); n++)
        {
            if( nodes.find(face.nodes[n]) == nodes.end() )
                on_base.push_back(false);
            else
                on_base.push_back(true);
        }
        // edge (0, 1)
        if( (on_base[0] && on_base[1]) == false && (on_base[0] || on_base[1]) == true )
        {
            edges.insert( std::make_pair(face.nodes[0] , face.nodes[1]) );
            edges.insert( std::make_pair(face.nodes[1] , face.nodes[0]) );
        }

        // edge (1, 2)
        if( (on_base[1] && on_base[2]) == false && (on_base[2] || on_base[1]) == true )
        {
            edges.insert( std::make_pair(face.nodes[1] , face.nodes[2]) );
            edges.insert( std::make_pair(face.nodes[2] , face.nodes[1]) );
        }

        // edge (2, 3)
        if( (on_base[2] && on_base[3]) == false && (on_base[3] || on_base[2]) == true )
        {
            edges.insert( std::make_pair(face.nodes[2] , face.nodes[3]) );
            edges.insert( std::make_pair(face.nodes[3] , face.nodes[2]) );
        }

        // edge (3, 0)
        if( (on_base[3] && on_base[0]) == false && (on_base[0] || on_base[3]) == true )
        {
            edges.insert( std::make_pair(face.nodes[3] , face.nodes[0]) );
            edges.insert( std::make_pair(face.nodes[0] , face.nodes[3]) );
        }
    }

    // all the nodes
    cell.nodes = face.nodes;
    if(fbase<0)  std::reverse(cell.nodes.begin(), cell.nodes.end());

    for(size_t i=0; i<3; i++)
        cell.nodes.push_back(edges.find(cell.nodes[i])->second);
}



void
FluentMesh::replace_all(
    std::string& str,
    const std::string& from,
    const std::string& to)
{
    size_t start_pos = 0;
    while((start_pos = str.find(from, start_pos)) != std::string::npos)
    {
        str.replace(start_pos, from.length(), to);
        start_pos += to.length(); // Handles case where 'to' is a substring of 'from'
    }
}



int
FluentMesh::hex_string_to_int(
    const std::string& string)
{
    return (int)strtol(string.c_str(), 0, 16);
}



int
FluentMesh::dec_string_to_int (
    const std::string& string)
{
    return (int)strtol(string.c_str(), 0, 10);
}



double
FluentMesh::string_to_double(
    const std::string& string)
{
    return atof(string.c_str());
}



void
FluentMesh::unsorted_remove_duplicates(
    std::vector<int>& numbers)
{
    std::set<int> seenNums; //log(n) existence check

    std::vector<int>::iterator itr = numbers.begin();
    while (itr != numbers.end())
    {
        if(seenNums.find(*itr) != seenNums.end()) //seen? erase it
        {
            itr = numbers.erase(itr); //itr now points to next element
        }
        else
        {
            seenNums.insert(*itr);
            itr++;
        }
    }
}

} //namespace FreeMAPs
